Source code for hysop.backend.device.codegen.base.variables

# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import re, copy
import numpy as np

from hysop.tools.htypes import to_list, check_instance, first_not_None
from hysop.testsenv import __HAS_OPENCL_BACKEND__

if __HAS_OPENCL_BACKEND__:
    import hysop.backend.device.opencl.opencl_types

from hysop.backend.device.codegen.base.utils import VarDict

if __HAS_OPENCL_BACKEND__:
    from hysop.backend.device.opencl.opencl_types import TypeGen, OpenClTypeGen

    # opencl extras
    from hysop.backend.device.opencl.opencl_types import cl_type_to_dtype

_ctype_to_dtype = {
    "bool": np.bool_,
    "char": np.int8,
    "short": np.int16,
    "int": np.int32,
    "long": np.int64,
    "long int": np.int64,
    "long long int": np.int64,
    "signed char": np.int8,
    "signed short": np.int16,
    "signed int": np.int32,
    "signed long": np.int64,
    "signed long int": np.int64,
    "signed long long int": np.int64,
    "uchar": np.uint8,
    "ushort": np.uint16,
    "uint": np.uint32,
    "ulong": np.uint64,
    "unsigned char": np.uint8,
    "unsigned short": np.uint16,
    "unsigned int": np.uint32,
    "unsigned long int": np.uint64,
    "unsigned long long int": np.uint64,
    "size_t": np.uint64,  # warning: host and/or device can be 32bits, do not use in kernel arguments
    "ptrdiff_t": np.int64,  # warning: host and/or device can be 32bits, do not use in kernel arguments
    "half": np.float16,
    "float": np.float32,
    "double": np.float64,
    "long double": np.longdouble,
    "float2": np.complex64,
    "double2": np.complex128,
}

_dtype_to_ctype = {
    np.bool_: "bool",
    np.int8: "char",
    np.int16: "short",
    np.int32: "int",
    np.int64: "long",
    np.uint8: "uchar",
    np.uint16: "ushort",
    np.uint32: "uint",
    np.uint64: "ulong",
    np.float16: "half",
    np.float32: "float",
    np.float64: "double",
    np.longdouble: "long double",
    np.complex64: "float2",
    np.complex128: "double2",
}


[docs] def dtype_to_ctype(dtype): from hysop.tools.numerics import get_dtype dtype = get_dtype(dtype) if dtype not in _dtype_to_ctype.keys(): msg = f"Unknown dtype {dtype}." raise ValueError(msg) else: return _dtype_to_ctype[dtype]
[docs] def ctype_to_dtype(ctype): if ctype not in _ctype_to_dtype.keys(): from hysop.backend.device.opencl.opencl_types import np_dtype return np_dtype(ctype) else: return _ctype_to_dtype[ctype]
[docs] def register_ctype_dtype(ctype, dtype): if dtype not in _dtype_to_ctype.keys(): _dtype_to_ctype[dtype] = ctype if ctype not in _ctype_to_dtype.keys(): _ctype_to_dtype[ctype] = dtype
[docs] class CodegenVariable: def __init__( self, name, ctype, typegen, storage=None, const=False, volatile=False, static=False, add_impl_const=False, nl=None, align=None, ptr=False, ptr_restrict=None, ptr_volatile=None, ptr_const=None, value=None, svalue=None, init=None, symbolic_mode=False, struct_var=None, ): check_instance(typegen, TypeGen) check_instance(name, str) check_instance(ctype, str) check_instance(storage, str, allow_none=True) assert len(name) > 0 assert len(ctype) > 0 assert (storage is None) or len(ctype) > 0 check_instance(const, bool) check_instance(volatile, bool) check_instance(add_impl_const, bool) check_instance(nl, bool, allow_none=True) check_instance(align, bool, allow_none=True) self.name = name self.ctype = ctype self.typegen = typegen self.const = const self.volatile = volatile self.add_impl_const = add_impl_const self.storage = storage self.align = align self.static = static self.declared = False self.nl = nl if (nl is not None) else (storage is not None) self.struct_var = struct_var self.symbolic_mode = symbolic_mode # pointer if isinstance(ptr, bool): is_ptr = ptr ptr_level = int(ptr) else: check_instance(ptr, int) is_ptr = True ptr_level = ptr del ptr if is_ptr: ptr_restrict = [] if (ptr_restrict is None) else to_list(ptr_restrict) ptr_const = [] if (ptr_const is None) else to_list(ptr_const) ptr_volatile = [] if (ptr_volatile is None) else to_list(ptr_volatile) _len = max(len(ptr_restrict), len(ptr_const), len(ptr_volatile)) assert _len <= ptr_level ptr_restrict = np.asarray( ptr_restrict + [False] * (ptr_level - len(ptr_restrict)) ) ptr_volatile = np.asarray( ptr_volatile + [False] * (ptr_level - len(ptr_volatile)) ) ptr_const = np.asarray(ptr_const + [False] * (ptr_level - len(ptr_const))) else: assert ptr_restrict is None assert ptr_volatile is None assert ptr_const is None self.is_ptr = is_ptr self.ptr_level = ptr_level self.ptr_restrict = ptr_restrict self.ptr_const = ptr_const self.ptr_volatile = ptr_volatile # value and svalue if value is None: value = None elif np.isscalar(value): if ctype == "fbtype": dtype = self.typegen.floatn(1) else: dtype = ctype_to_dtype(ctype) value = np.asarray([value], dtype=dtype)[0] elif value.__class__ in [list, tuple]: dtype = ctype_to_dtype(ctype) try: value = np.asarray(value, dtype=dtype) except: value = np.asarray(value, dtype=object) elif isinstance(value, np.ndarray): try: dtype = ctype_to_dtype(ctype) value = np.asarray(value, dtype=dtype).copy() except: value = value.copy() else: pass self.value = value self.svalue = svalue self.init = init # check if add_impl_const: if not is_ptr: if const: msg = "Variable {} is const and add_impl_const has been specified!" msg = msg.format(name) raise ValueError(msg) elif len(ptr_const) > 0: if ptr_const[-1]: msg = "Variable {} has ptr_const[-1]=True and add_impl_const has been specified!" msg = msg.format(name) raise ValueError(msg)
[docs] def nv_replace(self, old, new): name = self.name.replace(old, new) return self.newvar(name)
[docs] def copy(self): return self.newvar(name=self.name)
@property def dim(self): return self.typegen.components(self.ctype) @property def basetype(self): return self.typegen.basetype(self.ctype)
[docs] def newvar( self, name, nl=False, typegen=None, storage=None, value=None, svalue=None, init=None, ctype=None, const=None, volatile=None, add_impl_const=None, ptr=None, ptr_restrict=None, ptr_volatile=None, ptr_const=None, cls=None, **kwds, ): ctype = self.ctype if (ctype is None) else ctype const = self.const if (const is None) else const volatile = self.volatile if (volatile is None) else volatile add_impl_const = ( self.add_impl_const if (add_impl_const is None) else add_impl_const ) storage = self.storage if (storage is None) else storage typegen = self.typegen if (typegen is None) else typegen cls = self.__class__ if (cls is None) else cls if ptr is None: ptr = self.ptr_level if (ptr is None) else ptr ptr_const = self.ptr_const if (ptr_const is None) else ptr_const ptr_volatile = self.ptr_volatile if (ptr_volatile is None) else ptr_volatile ptr_restrict = self.ptr_restrict if (ptr_restrict is None) else ptr_restrict return cls( name=name, nl=nl, value=value, svalue=svalue, init=init, ctype=ctype, storage=storage, const=const, volatile=volatile, add_impl_const=add_impl_const, ptr=ptr, ptr_restrict=ptr_restrict, ptr_volatile=ptr_volatile, ptr_const=ptr_const, typegen=typegen, **kwds, )
[docs] def alias(self, varname, **kwds): if self.symbolic_mode: init = self.access_prefix(access=False) return self.newvar(name=varname, init=init, **kwds) else: value = self.value return self.newvar(name=varname, value=value, init=None, **kwds)
[docs] def pointer_alias(self, name, ctype, **kargs): handle = self.newvar( name=name, ctype=ctype, init="({})({})".format(self.full_ctype(cast=True, ctype=ctype), self), **kargs, ) return handle
[docs] def pointer( self, name, ptr_level, ptr_const=None, ptr_volatile=None, ptr_restrict=None, add_impl_const=False, with_init=True, **kargs, ): ptr_const = [False] * ptr_level if (ptr_const is None) else to_list(ptr_const) ptr_volatile = ( [False] * ptr_level if (ptr_volatile is None) else to_list(ptr_volatile) ) ptr_restrict = ( [False] * ptr_level if (ptr_restrict is None) else to_list(ptr_restrict) ) assert ptr_level > 0 assert len(ptr_const) == ptr_level assert len(ptr_volatile) == ptr_level assert len(ptr_restrict) == ptr_level if self.is_ptr: ptr_level = self.ptr_level + ptr_level ptr_const = self.ptr_const + ptr_const ptr_volatile = self.ptr_volatile + ptr_volatile ptr_restrict = self.ptr_restrict + ptr_restrict if with_init: init = f"&{self.name}" else: init = None return self.newvar( name=name, ptr=ptr_level, ptr_const=ptr_const, ptr_volatile=ptr_volatile, ptr_restrict=ptr_restrict, init=init, add_impl_const=add_impl_const, **kargs, )
[docs] def base_ctype( self, storage=None, ctype=None, const=None, volatile=None, impl=True, align=None, add_impl_const=None, static=None, ): align = first_not_None(align, self.align, False) storage = self.storage if (storage is None) else storage ctype = self.ctype if (ctype is None) else ctype volatile = self.volatile if (volatile is None) else volatile static = self.static if (static is None) else static static |= (storage is not None) and ("__constant" in storage) if const is None: const = self.const if impl and (not self.is_ptr) and (not const): const = ( self.add_impl_const if (add_impl_const is None) else add_impl_const ) const |= (storage is not None) and ("__constant" in storage) base_ctype = "{storage}${static}${const}${volatile}${ctype}".format( storage=f"{storage} " if (storage is not None) else "", static="static " if static else "", const="const " if const else "", volatile="volatile " if volatile else "", ctype=ctype + " ", ) if not align: base_ctype = base_ctype.replace("$", "") return base_ctype.strip()
[docs] def ptr_ctype(self, impl=True, add_impl_const=None, cast=False): if self.is_ptr: ptrs = [] add_impl_const = ( self.add_impl_const if (add_impl_const is None) else add_impl_const ) for i, (c, v, r) in enumerate( zip(self.ptr_const, self.ptr_volatile, self.ptr_restrict) ): if i == self.ptr_level - 1: c = c or (impl and add_impl_const) ptr = " $*{const}${volatile}${restrict}".format( const="const " if (c and not cast) else "", volatile="volatile " if (v and not cast) else "", restrict="restrict " if (r and not cast) else "", ) ptrs.append(ptr) ptr_ctype = "".join(ptrs) else: ptr_ctype = "" return ptr_ctype
[docs] def full_ctype( self, storage=None, ctype=None, const=None, volatile=None, impl=True, multidecl=False, align=None, cast=False, add_impl_const=None, **kwds, ): align = first_not_None(align, self.align, False) if multidecl: base_ctype = "" else: base_ctype = self.base_ctype( storage, ctype, const, volatile, impl, align, add_impl_const=add_impl_const, **kwds, ) if len(base_ctype) == 0: msg = f"Failed to get base ctype in {self.__class__}." raise RuntimeError(msg) ptr_ctype = self.ptr_ctype(impl=impl, add_impl_const=add_impl_const, cast=cast) full_ctype = f"{base_ctype}{ptr_ctype}" if not align: full_ctype = full_ctype.replace("$", "") else: full_ctype = full_ctype.replace("$ ", "$") return full_ctype.strip()
[docs] def argument(self, impl, nl=None, name=None, **kargs): name = self.name if (name is None) else name nl = self.nl if (nl is None) else nl return "{} {name}{nl}".format( self.full_ctype(impl=impl, **kargs), name=name, nl="\n" if self.nl else "" )
[docs] def is_symbolic(self): return (self.value is None) or self.symbolic_mode
[docs] def in_struct(self): return self.struct_var is not None
[docs] def known(self): return self.value is not None
[docs] def force_symbolic(self, force=True): if force is None: return self.symbolic_mode = force
[docs] def set_value(self, val): assert not self.known(), f"Value was already set in variable {self.name}!" self.value = val
[docs] def sval(self, symbolic=None): symbolic = self.is_symbolic() if (symbolic is None) else symbolic if symbolic: return self.access_prefix(access=False) else: if self.svalue is not None: return self.svalue elif isinstance(self.value, bool): return "true" if self.value else "false" elif self.value is not None: return self.typegen.dump(self.value) else: msg = "value and svalue are not defined." raise RuntimeError(msg)
[docs] def access_prefix(self, access): acc = "" if self.in_struct(): acc += self.struct_var.access_prefix(access=True) acc += self.name if access: acc += "->" if self.is_ptr else "." return acc
[docs] def decl_name(self): return self.name
[docs] def declare( self, codegen=None, align=None, multidecl=False, const=None, _const=None, init=None, compact=False, **kwds, ): # const means add_impl_const, ie. declare current variable as constant (not pointed types) # _const is the real const align = first_not_None(align, self.align, False) ctype = self.full_ctype( align=align, multidecl=multidecl, add_impl_const=const, const=_const, **kwds ) if (not multidecl) and len(ctype) == 0: msg = f"Failed to get full ctype in {self.__class__}." raise RuntimeError(msg) # static array ctype needs to be split name = self.decl_name() if init is False: msg = "Const variable should be initialized at declaration." assert (not self.const) or (not self.add_impl_const), msg init = None else: init = init if (init is not None) else self.init if (len(ctype) > 0) and ctype[-1] == "*": code = f"{ctype}${name}" else: code = f"{ctype} ${name}" if init is not None: if compact: code = f"{code}={init}" else: code = f"{code} $= {init}" elif self.known(): self.force_symbolic(False) sval = self.sval() if compact: code = f"{code}={sval}" else: code = f"{code} $= {sval}" if not multidecl: code += ";" self.force_symbolic() self.declared = True if not align: code = code.replace("$", "") if codegen is not None: codegen.append(code) return code.strip()
[docs] def affect(self, codegen=None, align=None, init=None, compact=False, i=None): align = first_not_None(align, self.align, False) msg = "Cannot affect a const variable." assert (not self.const) or (not self.add_impl_const), msg init = self.init if (init is None) else init if compact: code = "{}$={};" else: code = "{} $= {};" if i is None: var = self else: var = self[i] code = code.format(var, init) if not align: code = code.replace("$", "") if codegen is not None: codegen.append(code) return code
@property def dtype(self): return ctype_to_dtype(self.ctype) def __getitem__(self, ss): if self.is_ptr: return f"{self.name}[{ss}]" elif ss == 0: return self.__call__() else: assert self.is_ptr, f"{self.name} is not a pointer!" def __repr__(self): if self.is_symbolic(): return f"{self.name}({self.ctype})" else: return f"{self.name}({self.ctype},{self.value})" def __call__(self): return self.sval() def __str__(self): return self.sval()
[docs] class CodegenArray(CodegenVariable): @staticmethod def _initialize_rec( name, typegen, storage, ctype, const, volatile, shape, sshape, value, svalue, ptr_level, ptr_restrict, ptr_const, ptr_volatile, symbolic_mode, ): if value is None: return value, svalue s0 = shape[0] if ptr_level == 1: _name = name _value = value if svalue is not None: _svalue = "{ " + ", ".join(svalue) + " }" else: _shape = shape[1:] _sshape = sshape[1:] _ptr_level = ptr_level - 1 _ptr_const = ptr_const[:-1] _ptr_restrict = ptr_restrict[:-1] _ptr_volatile = ptr_volatile[:-1] _value = [None] * s0 _svalue = [None] * s0 for d in range(s0): _name = f"{name}_{d}" dvalue = value[d] dsvalue = svalue[d] val, sval = CodegenArray._initialize_rec( _name, typegen, storage, ctype, const, volatile, _shape, _sshape, dvalue, dsvalue, _ptr_level, _ptr_restrict, _ptr_const, _ptr_volatile, symbolic_mode, ) var = CodegenArray( name=_name, typegen=typegen, storage=storage, ctype=ctype, const=const, volatile=volatile, shape=_shape, sshape=_sshape, value=val, svalue=sval, dim=_ptr_level, ptr_const=_ptr_const, ptr_volatile=_ptr_volatile, ptr_restrict=_ptr_restrict, add_impl_const=False, symbolic_mode=False, struct_var=None, _direct_init=True, ) _value[d] = var _svalue[d] = "\n\t".join(sval.split("\n")) if svalue is None: _svalue = None else: _svalue = "{\n\t" + ",\n\t".join(_svalue) + "\n}" return _value, _svalue def __init__( self, name, ctype, typegen, storage=None, volatile=False, const=False, add_impl_const=False, dim=1, ptr_const=None, ptr_volatile=None, ptr_restrict=None, shape=None, sshape=None, value=None, svalue=None, symbolic_mode=False, struct_var=None, _direct_init=False, ): ptr_level = dim del dim if _direct_init: _value, _svalue = value, svalue else: ptr_const = [] if (ptr_const is None) else to_list(ptr_const) ptr_volatile = [] if (ptr_volatile is None) else to_list(ptr_volatile) ptr_restrict = [] if (ptr_restrict is None) else to_list(ptr_restrict) ptr_const += [False] * (ptr_level - len(ptr_const)) ptr_volatile += [False] * (ptr_level - len(ptr_volatile)) ptr_restrict += [False] * (ptr_level - len(ptr_restrict)) if value is not None: value = np.asarray(value) if value.ndim != ptr_level: raise ValueError("value array dim mismatch.") if shape is None: shape = value.shape else: assert value.shape == shape, "shape mismatch." if svalue is not None: svalue = np.asarray(svalue) if value is None: raise ValueError("value cannot be None when svalue is not None.") if svalue.ndim != ptr_level: raise ValueError("svalue input array dimension mismatch!") if svalue.shape != value.shape: raise ValueError( "Input array shape mismatch between value and svalue." ) elif value is not None: svalue = np.zeros_like(value, dtype=object) dtype = ctype_to_dtype(ctype) sview = svalue.flat for i, v in enumerate(value.flat): sview[i] = typegen.dump(v) if shape is not None: if len(shape) != ptr_level: raise ValueError("shape dim mismatch!") else: shape = (None,) * ptr_level shape = np.asarray(shape) if (sshape is None) and (shape[0] != None): sshape = [str(s) for s in shape] elif (sshape is not None) and len(sshape) != dim: raise ValueError("sshape dim mismatch!") else: sshape = (None,) * ptr_level sshape = np.asarray(sshape) _value, _svalue = CodegenArray._initialize_rec( name, typegen, storage, ctype, const, volatile, shape, sshape, value, svalue, ptr_level, ptr_restrict, ptr_const, ptr_volatile, symbolic_mode, ) super().__init__( name=name, storage=storage, ctype=ctype, typegen=typegen, value=_value, svalue=_svalue, const=const, add_impl_const=add_impl_const, volatile=volatile, ptr=ptr_level, ptr_restrict=ptr_restrict, ptr_const=ptr_const, ptr_volatile=ptr_volatile, symbolic_mode=symbolic_mode, struct_var=struct_var, ) self.shape = shape self.sshape = sshape
[docs] def decl_name(self): if self.shape is not None: static_array = [f"[{val}]" for val in self.shape] elif self.sshape is not None: static_array = [f"[{val}]" for val in self.sshape] else: static_array = [] return "{}{}".format(self.name, "".join(static_array))
[docs] def array_dim(self): if self.shape is not None: return self.shape.size if self.sshape is not None: return self.sshape.size msg = "unknown array dim." raise RuntimeError(msg)
[docs] def ptr_ctype(self, impl=True, add_impl_const=None, cast=False): if self.is_ptr: add_impl_const = ( self.add_impl_const if (add_impl_const is None) else add_impl_const ) dim = self.array_dim() ptr_const = self.ptr_const[dim:] ptr_volatile = self.ptr_volatile[dim:] ptr_restrict = self.ptr_restrict[dim:] ptrs = [] for i, (c, v, r) in enumerate(zip(ptr_const, ptr_volatile, ptr_restrict)): if i == self.ptr_level - 1: c = c or (impl and add_impl_const) ptr = " $*{const}${volatile}${restrict}".format( const="const " if (c and not cast) else "", volatile="volatile " if (v and not cast) else "", restrict="restrict " if (r and not cast) else "", ) ptrs.append(ptr) ptr_ctype = "".join(ptrs) else: ptr_ctype = "" return ptr_ctype
[docs] class CodegenVector(CodegenVariable): def __init__( self, name, ctype, dim, typegen, value=None, svalue=None, storage=None, const=False, volatile=False, ptr=False, ptr_const=None, ptr_volatile=None, ptr_restrict=None, add_impl_const=False, nl=None, symbolic_mode=False, struct_var=None, init=None, ): super().__init__( name=name, ctype=ctype, value=value, typegen=typegen, const=const, volatile=volatile, add_impl_const=add_impl_const, storage=storage, nl=nl, ptr=ptr, ptr_const=ptr_const, ptr_volatile=ptr_volatile, ptr_restrict=ptr_restrict, symbolic_mode=symbolic_mode, struct_var=struct_var, init=init, ) self.value = value self._dim = dim if value is not None: self.svalue = svalue if svalue else [typegen.dump(v) for v in value] else: assert not svalue self.svalue = None @property def dim(self): return self._dim
[docs] def sval(self, i=None): if i is not None: assert i < self.dim if self.is_symbolic(): return f"{self.access_prefix(False)}{self.name}[{i}]" else: return self.svalue[i] else: if self.is_symbolic(): return self.access_prefix(False) else: expansion = ",".join(self.svalue) return "{ " + expansion + " }"
def __getitem__(self, i): return self.sval(i) def __repr__(self): if self.is_symbolic(): return f"{self.name}({self.ctype})" else: vals = "[" + ",".join(self.svalue) + "]" return f"{self.name}({self.ctype},{vals})"
[docs] class CodegenVectorClBuiltin(CodegenVector): def __init__( self, name, btype, dim, typegen, value=None, access_mode=None, const=False, add_impl_const=False, storage=None, nl=None, init=None, symbolic_mode=False, struct_var=None, **kwds, ): factor = typegen.components(btype) btype = typegen.basetype(btype) dim *= factor if dim > 1: ctype = btype + str(dim) access_mode = access_mode if access_mode else ("pos" if dim <= 4 else "hex") msg = f"Wrong vector size {dim}" assert dim in typegen.vsizes msg = f"Invalid basetype {btype} for vector." assert btype in ( typegen.float_base_types + typegen.signed_base_types + typegen.unsigned_base_types ), msg msg = f"Invalid builtin type {ctype}." assert ctype in typegen.builtin_types, ctype else: # scalar type ctype = btype access_mode = None svalue = None if value is not None: dtype = ctype_to_dtype(btype) value = np.asarray(value, dtype) assert value.size == dim svalue = [typegen.dump(np.asarray([f], dtype=dtype)[0]) for f in value] super().__init__( name=name, ctype=ctype, dim=dim, typegen=typegen, value=value, svalue=svalue, const=const, add_impl_const=add_impl_const, storage=storage, nl=nl, symbolic_mode=symbolic_mode, struct_var=struct_var, init=init, ) self.btype = btype self.access_mode = access_mode
[docs] def newvar(self, name, btype=None, dim=None, **kwds): btype = first_not_None(btype, self.btype) dim = first_not_None(dim, self.dim) return super().newvar(name=name, btype=btype, dim=dim, **kwds)
[docs] def view(self, name, components, const=False): if isinstance(components, slice): start, stop, step = components.indices(self.dim) it = tuple(range(start, stop, step)) dim = len(it) elif components is Ellipsis: it = tuple(range(self.dim)) dim = self.dim else: raise ValueError( f"Unknown components type {components.__class__.__name__}." ) if dim > self.dim: raise ValueError("Dimension of view is greater than original vector!") return CodegenVectorClBuiltin( name=name, btype=self.btype, dim=dim, typegen=self.typegen, access_mode=self.access_mode, const=self.const or const, add_impl_const=self.add_impl_const, storage=self.storage, symbolic_mode=self.symbolic_mode, value=self.value[components] if (self.value is not None) else None, init=self[components], )
[docs] def set_value(self, value, svalue=None): assert value is not None value = to_list(value) dtype = ctype_to_dtype(self.btype) value = np.asarray(value, dtype).copy() if value.size != self.dim: raise ValueError("value dimension mismatch!") if (self.value != value).any(): if self.known(): msg = f"Value was already set in variable {self.name}!" raise RuntimeError(msg) self.value = value if svalue is not None: self.svalue = svalue else: self.svalue = [self.typegen.dump(f) for f in value]
[docs] def sval(self, i=None): if self.is_symbolic(): if (i is not None) and (self.dim > 1): return self.access_prefix(True) + self.typegen.vtype_access( i, self.dim, self.access_mode ) else: return self.access_prefix(False) else: if i is not None: return self.svalue[i] else: return self[:]
def __getitem__(self, key): dim = self.dim if isinstance(key, range): key = tuple(key) if isinstance(key, slice): ids = tuple(range(*key.indices(dim))) if self.declared and key.indices(dim) == (0, dim, 1): return self.name else: return self.__getitem__(ids) elif key is Ellipsis: return self.__getitem__(slice(None)) elif isinstance(key, (tuple, list)): if self.is_symbolic(): if self.dim > 1: mode = self.access_mode access = self.access_prefix(True) access += "s" if mode.lower() == "hex" else "" access += "".join( [self.typegen.vtype_component_adressing(i, mode) for i in key] ) else: assert len(key) == 1 and key[0] == 0 access = self.access_prefix(False) else: ctype = self.btype + (str(len(key)) if len(key) != 1 else "") value = [self.svalue[i] for i in key] return "({})({})".format(ctype, ",".join(value)) return access elif isinstance(key, (int, np.integer)): if key < 0: key += dim if key < 0 or key >= dim: raise IndexError(f"The index {key} is out of range.") return self.sval(key) else: msg = f"Invalid key type {type(key)}!" raise TypeError(msg)
[docs] def declare(self, codegen=None, init=None, **kargs): init = first_not_None(init, self.init) if isinstance(init, int): init = ",".join([self.typegen.dump(init) for _ in range(self.dim)]) init = f"({self.ctype})({init})" elif init.__class__ in [list, tuple, np.ndarray]: init = ",".join([self.typegen.dump(init[i]) for i in range(self.dim)]) init = f"({self.ctype})({init})" return super().declare(init=init, codegen=codegen, **kargs)
[docs] class CodegenVectorClBuiltinFunc(CodegenVectorClBuiltin): def __init__( self, fname, name, btype, dim, typegen, value=None, access_mode="pos", symbolic_mode=False, const=False, ): super().__init__( name=name, btype=btype, dim=dim, typegen=typegen, value=value, access_mode=access_mode, const=const, symbolic_mode=symbolic_mode, ) self.fname = fname
[docs] def fval(self, i=None): if i is None: value = [self.fval(i) for i in range(self.dim)] return "({})({})".format(self.ctype, ",".join(value)) else: assert i < self.dim return f"get_{self.fname}({i})"
[docs] def declare(self, codegen=None, init=None, **kargs): if init is False: init = None else: init = self.fval() if not self.known() else init return super().declare(init=init, codegen=codegen, **kargs)
[docs] class CodegenStruct(CodegenVariable): def __init__( self, name, struct, storage=None, const=False, volatile=False, ptr=False, ptr_const=None, ptr_volatile=None, ptr_restrict=None, add_impl_const=False, nl=None, symbolic_mode=False, struct_var=None, value=None, var_overrides=None, ): super().__init__( name=name, ctype=struct.ctype, typegen=struct.typegen, storage=storage, const=const, volatile=volatile, ptr=ptr, ptr_const=ptr_const, ptr_volatile=ptr_volatile, ptr_restrict=ptr_restrict, add_impl_const=add_impl_const, nl=nl, value=value, symbolic_mode=symbolic_mode, struct_var=struct_var, ) self.genvars(struct, var_overrides)
[docs] def newvar(self, *args, **kwds): kwds.setdefault("cls", CodegenVariable) return super().newvar(*args, **kwds)
[docs] def force_symbolic(self, force=True): if force is None: return super().force_symbolic(force) for k, var in self.vars.items(): var.force_symbolic(force)
def __getitem__(self, key): if key in self.vars: return self.vars[key] else: msg = "Unknown field {} in struct {}.\nAvailable fields are: {}" msg = msg.format(key, self.name, self.vars.keys()) raise KeyError(msg) def __getattr__(self, name): if name in self.vars: return self.__getitem__(name) else: raise AttributeError
[docs] def genvars(self, struct, var_overrides): if var_overrides is None: var_overrides = {} self.vars = VarDict() struct_vars = re.compile( r"\s+((?:struct\s+)?\w+)\s+((?:\s*\**(?:\w+)(?:\[\d+\])*[,;])+)" ) var_decl = re.compile(r"(\**)(\w+)((?:\[\d+\])*)") lines = struct.c_decl().split("\n") svalue = [] for l in lines: match = struct_vars.match(l) if match: ctype = match.group(1) variables = match.group(2).replace(";", "").split(",") for var in variables: match = var_decl.match(var) ptrs = match.group(1) fieldname = match.group(2) array = match.group(3) nptrs = len(ptrs) if ptrs else 0 narray = ( [int(x) for x in array[:-1].replace("[", "").split("]")] if array else None ) is_struct = struct.fields()[fieldname][0].fields is not None if nptrs > 0: raise RuntimeError( "Pointer variables are not supported yet (opencl unsupported)!" ) elif narray is not None: raise RuntimeError( "Static array variables are not supported yet!" ) if self.value is not None: if fieldname in var_overrides.keys(): pass elif fieldname not in self.value.keys(): raise ValueError( f"Value was given but field {fieldname} is not present." ) else: field_value = self.value[fieldname] else: field_value = None if fieldname in var_overrides: var = var_overrides[fieldname] var.struct_var = self var.const = self.const elif ( ctype in hysop.backend.device.opencl.opencl_types.builtin_types ): tg = struct.typegen btype = tg.basetype(ctype) dim = tg.components(ctype) if (field_value is not None) and not isinstance( field_value, np.ndarray ): field_value = np.asarray([field_value], dtype=btype) var = CodegenVectorClBuiltin( name=fieldname, btype=btype, dim=dim, access_mode=None, const=self.const, value=field_value, typegen=struct.typegen, struct_var=self, ) else: var = CodegenVariable( name=fieldname, ctype=ctype, const=self.const, value=field_value, typegen=struct.typegen, struct_var=self, ) var.force_symbolic(False) decl = var() decl = decl.replace("\n", "\n\t") sval = f".{fieldname} $= {decl}" svalue.append(sval) var.force_symbolic(self.symbolic_mode) self.vars[fieldname] = var if self.known(): self.svalue = "{\n\t" + ",\n\t".join(svalue) + "\n}" def __getitem__(self, key): return self.vars[key]
if __name__ == "__main__": from hysop.backend.device.codegen.base.test import _test_typegen as test_typegen tg = test_typegen("float") print(":: ARRAYS ::") var = CodegenArray( name="A0", ctype="float", dim=3, storage="__constant", value=[ [[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[10, 11, 12], [13, 14, 15], [16, 17, 18]], ], typegen=tg, ) print(var.declare()) # runtime known variable print(":: SYMBOLIC VECTOR ::") var = CodegenVectorClBuiltin("gid", "int", 3, tg) print(var()) for i in range(var.dim): print(var[i]) print(var[:]) print(var.declare()) print() # change access mode print(":: ACCESS MODES ::") var = CodegenVectorClBuiltin("ids", "int", 4, tg, access_mode="pos") print(var[::2]) var = CodegenVectorClBuiltin("ids", "int", 16, tg, access_mode="hex") print(var[::2]) var = CodegenVectorClBuiltin("ids", "int", 16, tg, access_mode="HEX") print(var[::2]) print() # compilation time known variable print(":: KNOWN VECTOR ::") var = CodegenVectorClBuiltin("lid", "int", 3, tg, value=(256, 512, 1024)) print(var()) for i in range(var.dim): print(var[i]) print(var[1:3]) print(var.declare(const=True)) print() # force the use of symbolic value print(":: FORCE SYMBOLIC ACCESS ::") var = CodegenVectorClBuiltin("id", "int", 4, tg, value=(1, 2, 3, 4)) print(var.declare()) var.force_symbolic() print(var[:]) var.force_symbolic(False) print(var[:]) print() # default decimal float dumper print(":: DEFAULT FLOAT DUMPER ::") var = CodegenVectorClBuiltin("size", "float", 4, tg, value=(1.0, 2.0, 4.0, 8.0)) print(var()) for i in range(var.dim): print(var[i]) print(var[1:3]) print(var.declare(storage="__constant")) print() # hexadecimal deciml float dumper print(":: HEXADECIMAL FLOAT DUMPER ::") var = CodegenVectorClBuiltin( "size_hex", "float", 4, value=(1.0, 2.0, 4.0, 8.0), typegen=test_typegen("float", float_dump_mode="hex"), ) print(var()) for i in range(var.dim): print(var[i]) print(var[1:3]) print(var.declare()) print() # bultin opencl functions for fname, name in [ ("global_size", "gsize"), ("local_size", "lsize"), ("global_id", "gid"), ("local_id", "lid"), ]: var = CodegenVectorClBuiltinFunc(fname, name, "int", 3, tg) print(var.declare())